<?php

/**
 * 战斗模型
 *
 * @author JiangJian <silverd@sohu.com>
 * $Id: Fight.php 143 2012-10-22 02:18:09Z silverd30@gmail.com $
 */

class Model_Fight extends Core_Model_Abstract
{
    const
        MAX_ROUNDS      = 20,   // 最多几个回合
        WIN             = 1,    // 胜利
        LOSE            = -1,   // 失败
        DRAW            = 0,    // 平手
        USER_MIN_HP     = 25,
        USER_MIN_ENERGY = 1;

    private $_self;
    private $_enemy;
    private $_selfShips   = array();
    private $_enemyShips  = array();
    private $_fightResult = 0;

    /**
     * 初始化一场战斗
     *
     * @param array $self
     * @param array $enemy
     */
    public function __construct($self, $enemy)
    {
        if (!$self || !$enemy) {
            throw new Core_Exception_Logic('战斗初始化失败：用户信息不存在');
        }

        $this->_self  = $self;
        $this->_enemy = $enemy;

        // 前提检测
        $this->_initCheck();

        // 船只初始化
        $this->_initShips();

        // 战斗记录器
        $this->_recorder = new Model_Fight_Recorder();
        $this->_recorder->init($this->_self, $this->_enemy);
    }

    private function _initCheck()
    {
        if ($this->_self['hp'] < self::USER_MIN_HP) {
            // throw new Core_Exception_Logic('你的生命值太低，无法参与战斗');
        }

        if ($this->_enemy['hp'] < self::USER_MIN_HP) {
            // throw new Core_Exception_Logic('对方生命值太低，无法参与战斗');
        }

        if ($this->_self['energy'] < self::USER_MIN_ENERGY) {
            throw new Core_Exception_Logic('你的精力不足，无法参与战斗');
        }

        if ($this->_self['status'] != Model_User::STATUS_SAILING) {
            throw new Core_Exception_Logic('你尚未出海，无法参与战斗');
        }

        if ($this->_enemy['status'] != Model_User::STATUS_SAILING) {
            // throw new Core_Exception_Logic('对方尚未出海，无法参与战斗');
        }
    }

    /**
     * 初始化、计算双方的船只属性值（含Buff）
     */
    private function _initShips()
    {
        // 我方舰船信息
        $this->_selfShips = $this->_self->getShipList();
        if (!$this->_selfShips) {
            throw new Core_Exception_Logic('你没有任何舰船，无法战斗');
        }

        // 敌方舰船信息
        $this->_enemyShips = $this->_enemy->getShipList();
        if (!$this->_enemyShips) {
            throw new Core_Exception_Logic('对方没有任何舰船，无法战斗');
        }

        // TODO buff
        foreach ($this->_selfShips as &$ship) {
        }

        // TODO buff
        foreach ($this->_enemyShips as &$ship) {
        }

        // 随机布阵
        // 注：打散后key会从0开始重排
        shuffle($this->_selfShips);
        shuffle($this->_enemyShips);
    }

    public function getSelfShips()
    {
        return $this->_selfShips;
    }

    public function getEnemyShips()
    {
        return $this->_enemyShips;
    }

    /**
     * 战斗进行（外观模式）
     *
     * @return const
     */
    public function process()
    {
        $this->_fightResult = $this->_fight();

        $this->_recorder->setFightResult($this->_fightResult);

        return $this->_fightResult;
    }

    /**
     * 战斗进行
     *
     * @return const Model_Fight::WIN/LOSE/DRAW
     */
    private function _fight()
    {
        $selfAimTarget  = 0; // 我方瞄准攻击的目标（默认是敌方的第1艘船）
        $enemyAimTarget = 0; // 敌方瞄准攻击的目标（默认是我方的第1艘船）

        $shipCount = max(count($this->_selfShips), count($this->_enemyShips));

        // 循环N个回合
        for ($round = 1; $round <= self::MAX_ROUNDS; $round++) {

            // 每艘船交错轮流开火
            for ($shipNo = 0; $shipNo < $shipCount; $shipNo++) {

                // 我船攻击敌船
                if (isset($this->_selfShips[$shipNo])) {

                    // 敌船被击沉，我方攻击目标转移到下一艘
                    if (!isset($this->_enemyShips[$selfAimTarget])) {
                        // 我方胜利：没有攻击目标了（敌船全被击沉）
                        if (++$selfAimTarget >= $shipCount) {
                            return self::WIN;
                        }
                    }

                    // 我船开火攻击
                    $this->_fire($this->_selfShips[$shipNo], $this->_enemyShips[$selfAimTarget], 'self', $round);
                }

                // 敌船攻击我船
                if (isset($this->_enemyShips[$shipNo])) {

                    // 我船被击沉，敌方攻击目标转移到下一艘
                    if (!isset($this->_selfShips[$enemyAimTarget])) {
                        // 敌方胜利：没有攻击目标了（我船全被击沉）
                        if (++$enemyAimTarget >= $shipCount) {
                            return self::LOSE;   // 敌方的胜利，即我方的失败
                        }
                    }

                    // 敌船开火攻击
                    $this->_fire($this->_enemyShips[$shipNo], $this->_selfShips[$enemyAimTarget], 'enemy', $round);
                }
            }

            // 我方胜利：敌方船全被击沉
            if (count($this->_enemyShips) < 1) {
                return self::WIN;
            }

            // 我方失败：我方船全被击沉
            if (count($this->_selfShips) < 1) {
                return self::LOSE;
            }
        }

        // 双方战平：N回合内仍未见胜负
        return self::DRAW;
    }

    /**
     * 单次开炮攻击
     *
     * @param array &$attackerShip
     * @param array &$defenderShip
     * @param string $attackerType 发起进攻者 self|enemy 用于区分战斗记录文案
     * @param int $round 第几回合
     * @return void
     */
    private function _fire(&$attackerShip, &$defenderShip, $attackerType, $round)
    {
        // 对方被伤害（至少扣1滴血）
        $damage = max(1, $attackerShip['attack'] - $defenderShip['defense']);
        $defenderShip['hp'] = max(0, $defenderShip['hp'] - $damage);

        // 单条战斗记录文案模板
        $fireLogLangTpl = ($defenderShip['hp'] < 1 ? 'fire_sink_msg' : 'fire_msg') . '_' . $attackerType;

        // 当前攻防双方信息
        if ($attackerType == 'self') {
            $attackerUserName = $this->_self['user_name'];
            $defenderUserName = $this->_enemy['user_name'];
        } else {
            $attackerUserName = $this->_enemy['user_name'];
            $defenderUserName = $this->_self['user_name'];
        }

        $defenderType = $attackerType == 'self' ? 'enemy' : 'self';

        // 增加一条开炮攻击记录
        $this->_recorder->add(array(
            'round'          => $round,         // 第几回合
            'damage'         => $damage,        // 本次伤害点数
            'attacker_type'  => $attackerType,  // 攻击者：是我方or敌方 self/enemy
            'defender_type'  => $defenderType,  // 防守者：是我方or敌方 self/enemy

            // 主人公信息
            'att_user_name'  => $attackerUserName,
            'def_user_name'  => $defenderUserName,

            // 攻船信息
            'att_ship'       => array(
                'ship_id'    => $attackerShip['ship_id'],
                'ship_name'  => $attackerShip['ship_name'],
                'captain_id' => $attackerShip['captain_id'],
                'hp'         => $attackerShip['hp'],
            ),

            // 守船信息
            'def_ship'       => array(
                'ship_id'    => $defenderShip['ship_id'],
                'ship_name'  => $defenderShip['ship_name'],
                'captain_id' => $attackerShip['captain_id'],
                'hp'         => $defenderShip['hp'],
            ),

            // 详细文字
            'message' => lang($fireLogLangTpl, array(
                $attackerShip['ship_name'],
                $defenderShip['ship_name'],
                $damage,
                $defenderShip['ship_name']
            )),
        ));

        // 成功击沉对方
        if ($defenderShip['hp'] < 1) {
            $defenderShip = null;   // 这里不用 unset，因为需要在数组中保留该下标
        }
    }

    /**
     * 战后相关事宜处理
     * 例如：减生命值、加金钱、扣属性等
     *
     * @return string 最终战果影响文字
     */
    public function after()
    {
        // 战果提示文字
        $fightResustMsg = '';

        // 准备更新数组
        $setArrSelf = $setArrEnemy = array();

        // 扣精力
        $setArrSelf['energy'] = array('-', 1, 'POSITIVE_SUB');

        // 加经验
        $setArrSelf['exp'] = array('+', rand(1, 5));

        // 本次战斗打了几个回合
        $roundCount = $this->_recorder->roundCount();

        switch ($this->_fightResult) {

            // 我方胜利
            case self::WIN:

                // 胜利者从失败者身上抢银币
                $silverDeduct = floor($this->_enemy['silver'] * rand(7, 12) / 100);
                $setArrSelf['silver']  = array('+', $silverDeduct);
                $setArrEnemy['silver'] = array('-', $silverDeduct, 'POSITIVE_SUB');

                // 双方扣血值
                $setArrSelf['hp']  = array('-', rand(1, 5), 'POSITIVE_SUB');
                $setArrEnemy['hp'] = array('-', rand(16, 20), 'POSITIVE_SUB');

                // 最终战果影响文字
                $fightResustMsg = lang('fight_win', array(
                    $roundCount,
                    $this->_enemy['user_name'],
                    $silverDeduct,
                    $setArrSelf['exp'][1],
                    $setArrSelf['energy'][1],
                    $setArrSelf['hp'][1],
                    $this->_enemy['user_name'],
                    $setArrEnemy['hp'][1]
                ));

                break;

            // 我方失败
            case self::LOSE:

                // 胜利者从失败者身上抢银币
                $silverDeduct = floor($this->_self['silver'] * rand(7, 12) / 100);
                $setArrSelf['silver']  = array('-', $silverDeduct, 'POSITIVE_SUB');
                $setArrEnemy['silver'] = array('+', $silverDeduct);

                // 双方扣血值
                $setArrSelf['hp']  = array('-', rand(16, 20), 'POSITIVE_SUB');
                $setArrEnemy['hp'] = array('-', rand(1, 5), 'POSITIVE_SUB');

                // 最终战果影响文字
                $fightResustMsg = lang('fight_lose', array(
                    $roundCount,
                    $setArrSelf['energy'][1],
                    $setArrSelf['hp'][1],
                    $this->_enemy['user_name'],
                    $setArrEnemy['hp'][1]
                ));

                break;

            // 双方战平
            case self::DRAW:

                // 双方扣血值
                $setArrSelf['hp']  = array('-', rand(8, 12), 'POSITIVE_SUB');
                $setArrEnemy['hp'] = array('-', rand(8, 12), 'POSITIVE_SUB');

                // 最终战果影响文字
                $fightResustMsg = lang('fight_draw', array(
                    $roundCount,
                    $setArrSelf['energy'][1],
                    $setArrSelf['hp'][1],
                    $this->_enemy['user_name'],
                    $setArrEnemy['hp'][1]
                ));

                break;
        }

        // 统一更新我方属性
        if ($setArrSelf) {
            $this->_self->update($setArrSelf);
        }

        // 统一更新敌方属性
        if ($setArrEnemy) {
            $this->_enemy->update($setArrEnemy);
        }

        // 保存战斗记录
        $this->_recorder->setFightResultMsg($fightResustMsg)
                        ->save();

        // 最终战果影响文字
        return $fightResustMsg;
    }

    public function getFireLogs()
    {
        return $this->_recorder->get();
    }
}